package com.laolei.core.rest;

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashSet;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.SingletonBeanRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import com.laolei.core.cache.CacheService;
import com.laolei.core.cache.impl.DefaultCacheServiceImpl;
import com.laolei.core.rest.annotation.RestClient;
import com.laolei.core.utils.AnnotatedTypeScanner;
import com.laolei.core.utils.CglibProxy;

@Component
public class ScannerAndRegistrarBean implements ResourceLoaderAware, EnvironmentAware, ApplicationContextAware {
	private final static Logger LOGGER = LoggerFactory.getLogger("RestClient");

	AnnotatedTypeScanner typeScanner = new AnnotatedTypeScanner(RestClient.class);
	@Autowired(required = false)
	InvokeHandle InvokeHandle = new InvokeHandle();
	@Autowired(required = false)
	CacheService cacheService = new DefaultCacheServiceImpl();

	@Override
	public void setEnvironment(Environment environment) {
		typeScanner.setEnvironment(environment);
	}

	@Override
	public void setResourceLoader(ResourceLoader resourceLoader) {
		typeScanner.setResourceLoader(resourceLoader);
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
	}

	public Set<Class<?>> scanner(Set<String> packages) {
		if (packages.size() == 0)
			return new HashSet<>();
		Set<Class<?>> clazzs = typeScanner.findTypes(packages);
		return clazzs;
	}

	private <T> T cglibProxy(Class<T> clazz) {
		return CglibProxy.create(clazz, (target, method, args, methodProxy) -> {
			return this.recordHttp(method, args, false, () -> {
				return methodProxy.invokeSuper(target, args);
			});
		});
	}

	@SuppressWarnings("unchecked")
	private <T> T jdkProxy(Class<T> clazz) {
		return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] { clazz },
				(Object target, Method method, Object[] args) -> {
					return this.recordHttp(method, args, true, () -> {
						return ReflectionUtils.invokeMethod(method, target, args);
					});
				});
	}

	public Object recordHttp(Method method, Object[] args, boolean isInterface, ProxyInvoke proxyInvoke)
			throws Throwable {
		LOGGER.debug("HttpRequest代理请求");
		RestClientData resttData = AnnotationAnalysis.initMethodParameter(method, isInterface);
		if (resttData == null)
			return proxyInvoke.invoke();
		if (!resttData.isCache())
			return InvokeHandle.doHttpInvoke(proxyInvoke, args, resttData);
		return cacheService.goCache(resttData.getMethodKey(), args, resttData.getCachetimeout(),
				() -> InvokeHandle.doHttpInvoke(proxyInvoke, args, resttData));
	}

	public void registrarScannerBean(Set<String> packages, SingletonBeanRegistry beanRegistry) throws BeansException {
		Set<Class<?>> clazz = this.scanner(packages);
		clazz.forEach(claz -> {
			String simpleName = claz.getSimpleName();
			String beanName = Character.toLowerCase(simpleName.charAt(0)) + simpleName.substring(1);
			if (beanRegistry.containsSingleton(beanName))
				return;
			Object bean;
			if (claz.isInterface())
				bean = this.jdkProxy(claz);
			else {
				bean = this.cglibProxy(claz);
			}
			beanRegistry.registerSingleton(beanName, bean);
		});
	}
}
